import * as sinon from 'sinon';
import proxyquire from 'proxyquire';
import fs from 'fs';
import * as chai from 'chai';
import {executeWithMaxRetryThreeTimes} from "../src/build-push";

const expect = chai.expect;

function createDirectorySync(path: string): void {
    try {
        fs.mkdirSync(path, {recursive: true});
        console.log(`Directory created at: ${path}`);
    } catch (err) {
        console.error(`Failed to create directory at: ${path}`, err);
    }
}

describe('buildPushImage', () => {
    let sandbox: sinon.SinonSandbox;
    let buildPushImage: Function;
    let stepStub: {
        exec: sinon.SinonStub;
        artifact: {
            ArtifactDocker: Function;
            exportArtifactDocker: sinon.SinonStub;
        };
    };

    beforeEach(() => {
        sandbox = sinon.createSandbox();

        // Mock dependencies
        stepStub = {
            exec: sandbox.stub().resolves(0),
            artifact: {
                ArtifactDocker: function () {
                },
                exportArtifactDocker: sandbox.stub()
            }
        };

        const dockerToolkitMock = {
            formatContextPath: sandbox.stub().returns('/tmp/projectdir'),
            parseImage: sandbox.stub().returns('registry/namespace/repo:tag'),
            getTagFromImageId: sandbox.stub().returns('tag'),
            getDockerBuildArgs: sandbox.stub().returns(['build', '-t', 'registry/namespace/repo:tag', '.'])
        };

        const fsMock = {
            existsSync: sandbox.stub().returns(true),
            copyFileSync: sandbox.stub(),
            readFileSync: sandbox.stub().returns('FROM node\n')
        };

        const pathMock = {
            join: sandbox.stub().callsFake((...paths) => paths.join('/'))
        };

        // Proxyquire the module with mocked dependencies
        buildPushImage = proxyquire('../src/build-push', {
            '@flow-step/step-toolkit': stepStub,
            '@flow-step/dockertoolkit': dockerToolkitMock,
            'fs': fsMock,
            'path': pathMock
        }).buildPushImage;
    });

    afterEach(() => {
        sandbox.restore();
    });

    it('should build and push an image without errors', async () => {
        createDirectorySync('/tmp/projectdir');

        const params = {
            projectDir: '/tmp/projectdir',
            dockerfilePath: '/tmp/projectdir/fake-dockerfile-path',
            contextPath: '',
            image: 'registry/namespace/repo:tag',
            cacheType: 'no-cache',
            cacheImageId: '',
            options: '--platform linux/amd64',
            stepID: 'fake_step_id',
            artifact: 'my_image'
        };

        await buildPushImage(params);
        sinon.assert.calledWithMatch(stepStub.artifact.exportArtifactDocker, 'fake_step_id.image')
        sinon.assert.calledWithMatch(stepStub.artifact.exportArtifactDocker, 'fake_step_id.artifact')
    });
});

describe('executeWithMaxRetryThreeTimes', () => {
    let stepStub: any;
    let executeWithMaxRetryThreeTimesProxy: any;

    beforeEach(() => {
        stepStub = {
            exec: {
                call: sinon.stub(),
            },
            info: sinon.stub(),
        };

        executeWithMaxRetryThreeTimesProxy = proxyquire('../src/build-push', {
            '@flow-step/step-toolkit': stepStub,
        }).executeWithMaxRetryThreeTimes;
    });

    afterEach(() => {
        sinon.restore();
    });

    it('should return the result code if the command succeeds', async () => {
        stepStub.exec.call.resolves(0);

        const result = await executeWithMaxRetryThreeTimesProxy(['build']);

        expect(result).to.equal(0);
        expect(stepStub.exec.call.calledOnce).to.be.true;
    });

    it.skip('should throw an error after reaching the maximum number of retries', async () => {
        stepStub.exec.call.rejects(new Error('ERROR: something else'));

        const result = await executeWithMaxRetryThreeTimes(['build'])
        expect(result).to.equal(1);
    });
});